package net.ge.mosq.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import net.ge.mosq.jdo.entity.AccountItem;
import net.ge.mosq.jdo.entity.CodeName;
import net.ge.mosq.jdo.factory.PMF;
import net.ge.mosq.service.AccountService;

/**
 * @author noodles.wumu@Gmail.com
 * @since 2012/06/27 17:50:07
 * @version 1.0
 */
public class AccountServiceImpl extends BaseService implements AccountService {

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#createAnAccount(net.ge.mosq.jdo.entity .AccountItem)
	 */
	public AccountItem createAnAccount(AccountItem anAccountItem) {

		PersistenceManager pm = PMF.get().getPersistenceManager();

		Date currentDate = new Date();
		anAccountItem.setCreateTime(currentDate);
		anAccountItem.setUpdateTime(currentDate);
		try {
			return pm.makePersistent(anAccountItem);
		} finally {
			pm.close();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#createManyAccount(java.util.Collection )
	 */
	public Collection<AccountItem> createManyAccount(Collection<AccountItem> anAccountItemList) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		for (AccountItem accountItem : anAccountItemList) {
			Date currentDate = new Date();
			accountItem.setCreateTime(currentDate);
			accountItem.setUpdateTime(currentDate);
		}
		try {
			return pm.makePersistentAll(anAccountItemList);
		} finally {
			pm.close();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#deleteAnAccount(net.ge.mosq.jdo.entity .AccountItem)
	 */
	public void deleteAnAccount(AccountItem aAccountItem) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			pm.deletePersistent(aAccountItem);
		} finally {
			pm.close();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#updateAnAccount(net.ge.mosq.jdo.entity .AccountItem)
	 */
	public synchronized void updateAnAccount(AccountItem anAccountItem) throws IllegalArgumentException {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			AccountItem accountItem = pm.getObjectById(AccountItem.class, anAccountItem.getId());
			if (accountItem.getUpdateCount() != anAccountItem.getUpdateCount()) {
				throw new IllegalArgumentException();
			}
			accountItem.setCode(anAccountItem.getCode());
			accountItem.setCurrency(anAccountItem.getCurrency());
			accountItem.setDescription(anAccountItem.getDescription());
			accountItem.setName(anAccountItem.getName());
			accountItem.setNotes(anAccountItem.getNotes());
			accountItem.setPlaceholder(anAccountItem.getPlaceholder());
			accountItem.setType(anAccountItem.getType());
			accountItem.setUpdateTime(new Date());
			accountItem.setUpdateCount(accountItem.getUpdateCount() + 1);
		} finally {
			pm.close();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#getAnAccountByKey(com.google.appengine .api.datastore.Key)
	 */
	public AccountItem getAnAccountByKey(Long id) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			return pm.getObjectById(AccountItem.class, id);
		} finally {
			pm.close();
		}
	}

	private void deepSearchAccount(List<AccountItem> source, AccountItem parent, int level) {
		for (AccountItem entity : source) {
			if (parent.getId() == entity.getParentId()) {
				if (parent.getChildenList() == null) {
					parent.setChildenList(new ArrayList<AccountItem>());
				}
				entity.setLevel(level);
				parent.getChildenList().add(entity);
				deepSearchAccount(source, entity, level + 1);
				parent.setTransactionSum(parent.getTransactionSum().add(entity.getTransactionSum()));
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#getAllAccountWithTransInfo(java.lang .String)
	 */
	@SuppressWarnings("unchecked")
	public synchronized List<AccountItem> getAllAccountWithTransInfo(String email) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		List<AccountItem> topItems = new ArrayList<AccountItem>();

		List<AccountItem> allList = new ArrayList<AccountItem>();
		Query query = null;
		try {
			String sql = "select from " + AccountItem.class.getName() + " where email == emailParam parameters String emailParam";
			query = pm.newQuery(sql);
			List<AccountItem> resultList = (List<AccountItem>) query.execute(email);

			TransactionService ts = new TransactionService();
			for (AccountItem accountItem : resultList) {
				accountItem.setTransactionSum(ts.getBalanceById(accountItem.getId()));
				allList.add(accountItem);
			}
		} finally {
			if (query != null)
				query.closeAll();
			pm.close();
		}

		for (AccountItem accountItem : allList) {
			if (accountItem.getParentId() == null) {
				topItems.add(accountItem);
				deepSearchAccount(allList, accountItem, 1);
			}
		}
		
		return topItems;
	}

	private void setBalance(AccountItem item, TransactionService ts) {
		if (item.getChildenList() != null && !item.getChildenList().isEmpty()) {
			BigDecimal sum = item.getTransactionSum();
			for (AccountItem sub : item.getChildenList()) {
				setBalance(sub, ts);
				sum = sum.add(sub.getTransactionSum());
			}
			item.setTransactionSum(sum);
		}
	}
	
	private void toFlat(List<AccountItem> result, List<AccountItem> allItems) {
		if (allItems != null && !allItems.isEmpty()) {
			for (AccountItem accountItem : allItems) {
				result.add(accountItem);
				toFlat(result, accountItem.getChildenList());
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#getAllAccountWithTransInfoFlat(java .lang.String)
	 */
	public synchronized List<AccountItem> getAllAccountWithTransInfoFlat(String email) {
		List<AccountItem> topItems = new ArrayList<AccountItem>();
		toFlat(topItems, getAllAccountWithTransInfo(email));
//		setBalance(topItems, ts)
		return topItems;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#getAccountByParentKeyWithTransInfo (com.google.appengine.api.datastore.Key, java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public List<AccountItem> getAccountByParentKeyWithTransInfo(Long parentId, String email) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		List<AccountItem> accList = null;
		Query query = null;
		try {
			query = pm.newQuery(AccountItem.class);
			if (email != null) {
				query.setFilter("parentId == '0' && email == emailParam");
				query.declareParameters("String emailParam");
			} else {
				query.setFilter("parentId == parentIdParam");
				query.declareParameters("Long parentIdParam");
			}
			if (email != null) {
				accList = (List<AccountItem>) query.execute(email);
			} else {
				accList = (List<AccountItem>) query.execute(parentId);
			}
		} finally {
			if (query != null)
				query.closeAll();
			pm.close();
		}
		return accList;
	}

	private void addCommonItems(List<AccountItem> items, Long parentId, PersistenceManager pm) {

		if (items != null) {
			for (AccountItem accountItem : items) {
				accountItem.setParentId(parentId);
				Date currentDate = new Date();
				accountItem.setCreateTime(currentDate);
				accountItem.setUpdateTime(currentDate);
				Long id = pm.makePersistent(accountItem).getId();
				addCommonItems(accountItem.getChildenList(), id, pm);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#addCommonItems(java.util.List)
	 */
	public void addCommonItems(List<AccountItem> items) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			this.addCommonItems(items, null, pm);
		} finally {
			pm.close();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#getAccountNameWithAllParentById(java .lang.Long)
	 */
	@Override
	public String getAccountNameWithAllParentById(Long id) {
		long id_ = id;
		AccountItem accountItem = this.getAnAccountByKey(id_);
		StringBuffer name = new StringBuffer(accountItem.getName());
		while (accountItem.getParentId() != null) {
			id_ = accountItem.getParentId();
			accountItem = this.getAnAccountByKey(id_);
			name.insert(0, accountItem.getName() + ":");
		}
		return name.toString();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see net.ge.mosq.service.AccountService#getAccountNameWithAllParent()
	 */
	@Override
	public List<CodeName> getAccountNameWithAllParent(String email) {
		List<AccountItem> aList = this.getAllAccountWithTransInfoFlat(email);
		List<CodeName> codeNameList = new ArrayList<CodeName>();
		for (AccountItem accountItem : aList) {
			codeNameList.add(new CodeName(String.valueOf(accountItem.getId()), this.getAccountNameWithAllParentById((accountItem.getId()))));
		}
		return codeNameList;
	}
}
